1   /*
2    * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.nio.cs.ext;
27  
28  import java.nio.ByteBuffer;
29  import java.nio.CharBuffer;
30  import java.nio.charset.Charset;
31  import java.nio.charset.CharsetDecoder;
32  import java.nio.charset.CharsetEncoder;
33  import java.nio.charset.CoderResult;
34  import java.util.Arrays;
35  import sun.nio.cs.Surrogate;
36  import static sun.nio.cs.CharsetMapping.*;
37  
38  public class HKSCS {
39  
40      public static class Decoder extends DoubleByte.Decoder {
41          static int b2Min = 0x40;
42          static int b2Max = 0xfe;
43  
44          private char[][] b2cBmp;
45          private char[][] b2cSupp;
46          private DoubleByte.Decoder big5Dec;
47  
48          protected Decoder(Charset cs,
49                            DoubleByte.Decoder big5Dec,
50                            char[][] b2cBmp, char[][] b2cSupp)
51          {
52              // super(cs, 0.5f, 1.0f);
53              // need to extends DoubleByte.Decoder so the
54              // sun.io can use it. this implementation
55              super(cs, 0.5f, 1.0f, null, null, 0, 0);
56              this.big5Dec = big5Dec;
57              this.b2cBmp = b2cBmp;
58              this.b2cSupp = b2cSupp;
59          }
60  
61          public char decodeSingle(int b) {
62              return big5Dec.decodeSingle(b);
63          }
64  
65          public char decodeBig5(int b1, int b2) {
66              return big5Dec.decodeDouble(b1, b2);
67          }
68  
69          public char decodeDouble(int b1, int b2) {
70              return b2cBmp[b1][b2 - b2Min];
71          }
72  
73          public char decodeDoubleEx(int b1, int b2) {
74              /* if the b2cSupp is null, the subclass need
75                 to override the methold
76              if (b2cSupp == null)
77                  return UNMAPPABLE_DECODING;
78               */
79              return b2cSupp[b1][b2 - b2Min];
80          }
81  
82          protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
83              byte[] sa = src.array();
84              int sp = src.arrayOffset() + src.position();
85              int sl = src.arrayOffset() + src.limit();
86  
87              char[] da = dst.array();
88              int dp = dst.arrayOffset() + dst.position();
89              int dl = dst.arrayOffset() + dst.limit();
90  
91              try {
92                  while (sp < sl) {
93                      int b1 = sa[sp] & 0xff;
94                      char c = decodeSingle(b1);
95                      int inSize = 1, outSize = 1;
96                      char[] cc = null;
97                      if (c == UNMAPPABLE_DECODING) {
98                          if (sl - sp < 2)
99                              return CoderResult.UNDERFLOW;
100                         int b2 = sa[sp + 1] & 0xff;
101                         inSize++;
102                         if (b2 < b2Min || b2 > b2Max)
103                             return CoderResult.unmappableForLength(2);
104                         c = decodeDouble(b1, b2);           //bmp
105                         if (c == UNMAPPABLE_DECODING) {
106                             c = decodeDoubleEx(b1, b2);     //supp
107                             if (c == UNMAPPABLE_DECODING) {
108                                 c = decodeBig5(b1, b2);     //big5
109                                 if (c == UNMAPPABLE_DECODING)
110                                     return CoderResult.unmappableForLength(2);
111                             } else {
112                                 // supplementary character in u+2xxxx area
113                                 outSize = 2;
114                             }
115                         }
116                     }
117                     if (dl - dp < outSize)
118                         return CoderResult.OVERFLOW;
119                     if (outSize == 2) {
120                         // supplementary characters
121                         da[dp++] = Surrogate.high(0x20000 + c);
122                         da[dp++] = Surrogate.low(0x20000 + c);
123                     } else {
124                         da[dp++] = c;
125                     }
126                     sp += inSize;
127                 }
128                 return CoderResult.UNDERFLOW;
129             } finally {
130                 src.position(sp - src.arrayOffset());
131                 dst.position(dp - dst.arrayOffset());
132             }
133         }
134 
135         protected CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) {
136             int mark = src.position();
137             try {
138                 while (src.hasRemaining()) {
139                     char[] cc = null;
140                     int b1 = src.get() & 0xff;
141                     int inSize = 1, outSize = 1;
142                     char c = decodeSingle(b1);
143                     if (c == UNMAPPABLE_DECODING) {
144                         if (src.remaining() < 1)
145                             return CoderResult.UNDERFLOW;
146                         int b2 = src.get() & 0xff;
147                         inSize++;
148                         if (b2 < b2Min || b2 > b2Max)
149                             return CoderResult.unmappableForLength(2);
150                         c = decodeDouble(b1, b2);           //bmp
151                         if (c == UNMAPPABLE_DECODING) {
152                             c = decodeDoubleEx(b1, b2);     //supp
153                             if (c == UNMAPPABLE_DECODING) {
154                                 c = decodeBig5(b1, b2);     //big5
155                                 if (c == UNMAPPABLE_DECODING)
156                                     return CoderResult.unmappableForLength(2);
157                             } else {
158                                 outSize = 2;
159                             }
160                         }
161                     }
162                     if (dst.remaining() < outSize)
163                         return CoderResult.OVERFLOW;
164                     if (outSize == 2) {
165                         dst.put(Surrogate.high(0x20000 + c));
166                         dst.put(Surrogate.low(0x20000 + c));
167                     } else {
168                         dst.put(c);
169                     }
170                     mark += inSize;
171                 }
172                 return CoderResult.UNDERFLOW;
173             } finally {
174                 src.position(mark);
175             }
176         }
177 
178         public CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) {
179             if (src.hasArray() && dst.hasArray())
180                 return decodeArrayLoop(src, dst);
181             else
182                 return decodeBufferLoop(src, dst);
183         }
184 
185         static void initb2c(char[][]b2c, String[] b2cStr)
186         {
187             for (int i = 0; i < b2cStr.length; i++) {
188                 if (b2cStr[i] == null)
189                     b2c[i] = DoubleByte.B2C_UNMAPPABLE;
190                 else
191                     b2c[i] = b2cStr[i].toCharArray();
192             }
193         }
194 
195     }
196 
197     public static class Encoder extends DoubleByte.Encoder {
198         private DoubleByte.Encoder big5Enc;
199         private char[][] c2bBmp;
200         private char[][] c2bSupp;
201 
202         protected Encoder(Charset cs,
203                           DoubleByte.Encoder big5Enc,
204                           char[][] c2bBmp,
205                           char[][] c2bSupp)
206         {
207             super(cs, null, null);
208             this.big5Enc = big5Enc;
209             this.c2bBmp = c2bBmp;
210             this.c2bSupp = c2bSupp;
211         }
212 
213         public int encodeBig5(char ch) {
214             return big5Enc.encodeChar(ch);
215         }
216 
217         public int encodeChar(char ch) {
218             int bb = c2bBmp[ch >> 8][ch & 0xff];
219             if (bb == UNMAPPABLE_ENCODING)
220                 return encodeBig5(ch);
221             return bb;
222         }
223 
224         public int encodeSupp(int cp) {
225             if ((cp & 0xf0000) != 0x20000)
226                 return UNMAPPABLE_ENCODING;
227             return c2bSupp[(cp >> 8) & 0xff][cp & 0xff];
228         }
229 
230         public boolean canEncode(char c) {
231             return encodeChar(c) != UNMAPPABLE_ENCODING;
232         }
233 
234         protected CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) {
235             char[] sa = src.array();
236             int sp = src.arrayOffset() + src.position();
237             int sl = src.arrayOffset() + src.limit();
238 
239             byte[] da = dst.array();
240             int dp = dst.arrayOffset() + dst.position();
241             int dl = dst.arrayOffset() + dst.limit();
242 
243             try {
244                 while (sp < sl) {
245                     char c = sa[sp];
246                     int inSize = 1;
247                     int bb = encodeChar(c);
248                     if (bb == UNMAPPABLE_ENCODING) {
249                         if (Character.isSurrogate(c)) {
250                             int cp;
251                             if ((cp = sgp().parse(c, sa, sp, sl)) < 0)
252                                 return sgp.error();
253                             bb = encodeSupp(cp);
254                             if (bb == UNMAPPABLE_ENCODING)
255                                 return CoderResult.unmappableForLength(2);
256                             inSize = 2;
257                         } else {
258                             return CoderResult.unmappableForLength(1);
259                         }
260                     }
261                     if (bb > MAX_SINGLEBYTE) {    // DoubleByte
262                         if (dl - dp < 2)
263                             return CoderResult.OVERFLOW;
264                         da[dp++] = (byte)(bb >> 8);
265                         da[dp++] = (byte)bb;
266                     } else {                      // SingleByte
267                         if (dl - dp < 1)
268                             return CoderResult.OVERFLOW;
269                         da[dp++] = (byte)bb;
270                     }
271                     sp += inSize;
272                 }
273                 return CoderResult.UNDERFLOW;
274             } finally {
275                 src.position(sp - src.arrayOffset());
276                 dst.position(dp - dst.arrayOffset());
277             }
278         }
279 
280         protected CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) {
281             int mark = src.position();
282             try {
283                 while (src.hasRemaining()) {
284                     int inSize = 1;
285                     char c = src.get();
286                     int bb = encodeChar(c);
287                     if (bb == UNMAPPABLE_ENCODING) {
288                         if (Character.isSurrogate(c)) {
289                             int cp;
290                             if ((cp = sgp().parse(c, src)) < 0)
291                                 return sgp.error();
292                             bb = encodeSupp(cp);
293                             if (bb == UNMAPPABLE_ENCODING)
294                                 return CoderResult.unmappableForLength(2);
295                             inSize = 2;
296                         } else {
297                             return CoderResult.unmappableForLength(1);
298                         }
299                     }
300                     if (bb > MAX_SINGLEBYTE) {  // DoubleByte
301                         if (dst.remaining() < 2)
302                             return CoderResult.OVERFLOW;
303                         dst.put((byte)(bb >> 8));
304                         dst.put((byte)(bb));
305                     } else {
306                         if (dst.remaining() < 1)
307                         return CoderResult.OVERFLOW;
308                         dst.put((byte)bb);
309                     }
310                     mark += inSize;
311                 }
312                 return CoderResult.UNDERFLOW;
313             } finally {
314                 src.position(mark);
315             }
316         }
317 
318         protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) {
319             if (src.hasArray() && dst.hasArray())
320                 return encodeArrayLoop(src, dst);
321             else
322                 return encodeBufferLoop(src, dst);
323         }
324 
325         static char[] C2B_UNMAPPABLE = new char[0x100];
326         static {
327             Arrays.fill(C2B_UNMAPPABLE, (char)UNMAPPABLE_ENCODING);
328         }
329 
330        static void initc2b(char[][] c2b, String[] b2cStr, String pua) {
331             // init c2b/c2bSupp from b2cStr and supp
332             int b2Min = 0x40;
333             Arrays.fill(c2b, C2B_UNMAPPABLE);
334             for (int b1 = 0; b1 < 0x100; b1++) {
335                 String s = b2cStr[b1];
336                 if (s == null)
337                     continue;
338                 for (int i = 0; i < s.length(); i++) {
339                     char c = s.charAt(i);
340                     int hi = c >> 8;
341                     if (c2b[hi] == C2B_UNMAPPABLE) {
342                         c2b[hi] = new char[0x100];
343                         Arrays.fill(c2b[hi], (char)UNMAPPABLE_ENCODING);
344                     }
345                     c2b[hi][c & 0xff] = (char)((b1 << 8) | (i + b2Min));
346                 }
347             }
348             if (pua != null) {        // add the compatibility pua entries
349                 char c = '\ue000';    //first pua character
350                 for (int i = 0; i < pua.length(); i++) {
351                     char bb = pua.charAt(i);
352                     if (bb != UNMAPPABLE_DECODING) {
353                         int hi = c >> 8;
354                         if (c2b[hi] == C2B_UNMAPPABLE) {
355                             c2b[hi] = new char[0x100];
356                             Arrays.fill(c2b[hi], (char)UNMAPPABLE_ENCODING);
357                         }
358                         c2b[hi][c & 0xff] = bb;
359                     }
360                     c++;
361                 }
362             }
363         }
364     }
365 }